Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Allow validators outside the active set to validate on consumer chains #1878

Closed

Conversation

p-offtermatt
Copy link
Contributor

@p-offtermatt p-offtermatt commented May 14, 2024

Description

Closes: #1913

This PR allows validators that are not participating in consensus on the provider chain to validate on consumer chains.
The extremely high level overview can be found here (I recommend reading this to get the high-level idea):
https://informalsystems.notion.site/Non-active-validators-on-consumer-chains-d4b700e19a7d447c8d9a2e69cca3e8d0?pvs=74

I recommend then reviewing the e2e test so that you can see what the new logic does in practice.
Other notable files:

  • validator_set_storage.go and provider_consensus.go: generalize the existing functionality for storing a consumer val set
  • wrapped_genutil/wrapped_staking: wrappers around these modules to prevent them from sending valupdates to comet, but still get their functionality otherwise
  • params.go: introduce a new parameter for the maximal consensus validators on the provider

Some changed I will make later but which should not block reviewing:

  • Rename ConsumerValidator and its fields to reflect that it also is used for the validator set on the provider: this change will make the diff much larger and less informative, so holding off on it
  • additional integration

Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • Included the correct type prefix in the PR title
  • Added ! to the type prefix if the change is state-machine breaking
  • Confirmed this PR does not introduce changes requiring state migrations, OR migration code has been added to consumer and/or provider modules

The LastConsensusValidatorSet needs to be initialized via migration to get a correct diff, this is still TODO

Maybe this should target a feature branch?

  • Provided a link to the relevant issue or specification
  • Followed the guidelines for building SDK modules
  • Included the necessary unit and integration tests
  • Added a changelog entry to CHANGELOG.md
  • Included comments for documenting Go code
  • Updated the relevant documentation or specification
  • Reviewed "Files changed" and left comments if necessary
  • Confirmed all CI checks have passed
  • If this PR is library API breaking, bump the go.mod version string of the repo, and follow through on a new major release

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed ! the type prefix if the change is state-machine breaking
  • confirmed this PR does not introduce changes requiring state migrations, OR confirmed migration code has been added to consumer and/or provider modules
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic
  • reviewed API design and naming
  • reviewed documentation is accurate
  • reviewed tests and test coverage

Summary by CodeRabbit

  • New Features

    • Introduced a new configuration for handling inactive validators, including detailed test scenarios.
    • Added functions to manage and interact with the last provider consensus validator set.
  • Enhancements

    • Updated module initialization and imports to improve modularity.
    • Enhanced logging for reward retrieval processes.
    • Refined validation and power calculation logic for better accuracy and efficiency.
  • Bug Fixes

    • Corrected variable names for consistency in the reward retrieval function.
  • Tests

    • Added comprehensive test cases for new configurations and validator behaviors.
    • Revised test functions to use new methods for creating and managing validators.
  • Documentation

    • Updated documentation to reflect new configuration options and functions.

@github-actions github-actions bot added the C:x/provider Assigned automatically by the PR labeler label May 14, 2024
x/ccv/provider/module.go Fixed Show fixed Hide fixed
store := ctx.KVStore(k.storeKey)
bz, err := validator.Marshal()
if err != nil {
panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err))

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods Warning

Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
iterator.Value()
var validator types.ConsumerValidator
if err := validator.Unmarshal(iterator.Value()); err != nil {
panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err))

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods Warning

Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
@github-actions github-actions bot added the C:Testing Assigned automatically by the PR labeler label May 14, 2024
@github-actions github-actions bot added the C:x/types Assigned automatically by the PR labeler label May 23, 2024
x/ccv/provider/module.go Fixed Show resolved Hide resolved
@p-offtermatt p-offtermatt changed the title feat!: Allow validators outside the active set to validate on consumer chains feat!: Allow validators outside the active set to validate on consumer chains (DO NOT MERGE) May 24, 2024
@p-offtermatt p-offtermatt changed the title feat!: Allow validators outside the active set to validate on consumer chains (DO NOT MERGE) feat!: Allow validators outside the active set to validate on consumer chains May 24, 2024
@p-offtermatt p-offtermatt changed the base branch from main to feat/non-active-validators May 24, 2024 10:08
@@ -498,7 +504,7 @@ func New(
// NOTE: Any module instantiated in the module manager that is later modified
// must be passed by reference here.
app.MM = module.NewManager(
genutil.NewAppModule(
wrapped_genutil.NewAppModule(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Genutil and staking need to be wrapped, because they send validator updates to CometBFT and we need to filter those.

@p-offtermatt p-offtermatt marked this pull request as ready for review May 24, 2024 10:21
@p-offtermatt p-offtermatt requested a review from a team as a code owner May 24, 2024 10:21
Copy link
Contributor

coderabbitai bot commented May 24, 2024

Walkthrough

Walkthrough

The changes introduced in this update modify how validators interact with the consensus and validation process in the Interchain Security Module. Validators not participating in the consensus on the provider chain can now validate consumer chains. This involves new functionalities to manage validator sets, import statements, module initialization, and adjustments in tests to accommodate the new features.

Changes

Files Change Summary
app/provider/app.go Imported wrapped_genutil and wrapped_staking, replaced staking with wrapped_staking in module functions.
proto/interchain_security/ccv/provider/v1/provider.proto Added new field max_provider_consensus_validators to the Params message.
tests/e2e/config.go Introduced InactiveValsCfg and InactiveValsConfig() for handling inactive validators' test configurations.
tests/e2e/main.go, steps_inactive_vals.go Added test cases and steps centered on inactive validators on the consumer chain.
tests/e2e/state.go Renamed variable delAddresss to delAddress and added logging in the getReward function.
testutil/keeper/**/* Updated MockStakingKeeper with new methods, added helper functions for creating validators, and adjusted test expectations.
x/ccv/provider/keeper/genesis.go Changed InitGenesis function to return []abci.ValidatorUpdate, handled staking validator sets, and restricted set size.
x/ccv/provider/keeper/**/*, x/ccv/provider/keeper/partial_set_security_test.go Adjusted various keeper methods to manage the provider consensus validator set, including setting and retrieving validators.
x/ccv/provider/keeper/relay.go Modified EndBlockVSU to return []abci.ValidatorUpdate and added ProviderValidatorUpdates.
x/ccv/provider/keeper/throttle.go, throttle_test.go Replaced calls to stakingKeeper.GetLastTotalPower with GetLastTotalProviderConsensusPower.
* ... * Various files adjusted to reflect changes in parameters and validator creation processes.

Sequence Diagram(s)

sequenceDiagram
    participant Provider
    participant Consumer
    participant Validator

    Note over Provider,Validator: Initial Setup
    Provider->>Validator: Determine Active Set
    Provider->Consumer: Send Validator Set (including inactive validators)

    Consumer->>Validator: Include Inactive Validators
    
    Note over Provider,Consumer: Ongoing Validation
    Consumer->>Provider: Sync status with inactive validators

    Note over Validator: Validation on Consumer Chain
    Validator->>Consumer: Validate blocks (inactive on Provider)
    Consumer->>Provider: Report validation status (periodically)
Loading

Assessment against linked issues

Objective Addressed Explanation
Allow validators not in provider consensus to validate consumer chains (#1913)
Maximal number of validators passed to the consensus engine on the provider (#1913)
Test and validate changes through e2e tests focusing on inactive validators (#1913)
Modifications ensuring validators' inclusion in both provider and consumer contexts (#1913)

The above changes and additions comprehensively address the objectives outlined in the linked issues, ensuring validators not in the provider's consensus can still validate consumer chains, alongside various tests and parameter adjustments to support this functionality.


Recent review details

Configuration used: .coderabbit.yml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between a7de72a and faff1a7.

Files selected for processing (2)
  • x/ccv/provider/keeper/relay.go (5 hunks)
  • x/ccv/provider/module.go (2 hunks)
Additional context used
Path-based instructions (2)
x/ccv/provider/module.go (1)

Pattern **/*.go: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.

x/ccv/provider/keeper/relay.go (1)

Pattern **/*.go: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.

GitHub Check: CodeQL
x/ccv/provider/module.go

[warning] 164-164: Panic in BeginBock or EndBlock consensus methods
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call

Additional comments not posted (2)
x/ccv/provider/module.go (1)

164-164: Address potential panic in EndBlockVSU.

The static analysis has flagged a potential panic flow in the EndBlockVSU method. It's crucial to investigate this path and ensure that there are proper error checks and handling mechanisms to prevent a panic.

Tools
GitHub Check: CodeQL

[warning] 164-164: Panic in BeginBock or EndBlock consensus methods
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call

x/ccv/provider/keeper/relay.go (1)

270-314: Optimize the ProviderValidatorUpdates method.

The method ProviderValidatorUpdates could be optimized by reducing the number of times validators are fetched and by simplifying the logic that determines the next set of validators.
[REFACTOR_Suggestion]

func (k Keeper) ProviderValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate {
    bondedValidators := k.stakingKeeper.GetBondedValidatorsByPower(ctx)
    currentValidators := k.GetLastProviderConsensusValSet(ctx)
    maxValidators := k.GetMaxProviderConsensusValidators(ctx)
    nextValidators, err := k.computeNextValidators(bondedValidators, currentValidators, maxValidators)
    if err != nil {
        k.Logger(ctx).Error("Failed to compute next validators", "error", err)
        return nil
    }

    k.SetLastProviderConsensusValSet(ctx, nextValidators)
    return DiffValidators(currentValidators, nextValidators)
}

+ func (k Keeper) computeNextValidators(bondedValidators []types.Validator, currentValidators []types.ConsumerValidator, maxValidators int64) ([]types.ConsumerValidator, error) {
    if int64(len(bondedValidators)) < maxValidators {
        maxValidators = int64(len(bondedValidators))
    }
    nextValidators := make([]types.ConsumerValidator, 0, maxValidators)
    for _, val := range bondedValidators[:maxValidators] {
        consAddr, err := val.GetConsAddr()
        if err != nil {
            return nil, fmt.Errorf("could not create validator: %w", err)
        }
        pubKey, err := val.TmConsPublicKey()
        if err != nil {
            return nil, fmt.Errorf("could not create consumer validator: %w", err)
        }
        nextValidator := types.ConsumerValidator{
            ProviderConsAddr:  consAddr,
            ConsumerPublicKey: &pubKey,
            Power:             k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()),
        }
        nextValidators = append(nextValidators, nextValidator)
    }
    return nextValidators, nil
}

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@mpoke mpoke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @p-offtermatt. The general logic LGTM. See my comment below.

Things missing:

  • migration (e.g., the LastProviderConsensusValSet needs to be initialized)
  • docs on integration (i.e., how to wire this in app.go, especially given the wrapped modules)
  • the provider module needs to implement the staking keeper interface, similarly to the consumer module (some of the modules that use the staking keeper need to use the provider keeper instead, e.g., mint, gov, distribution, IBC, ... )
  • finalized ADR

x/ccv/provider/types/params.go Show resolved Hide resolved
x/ccv/provider/module.go Fixed Show resolved Hide resolved
Comment on lines +111 to +112
wrapped_genutil "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_genutil"
wrapped_staking "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_staking"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to find better naming for these two modules.

app/provider/app.go Outdated Show resolved Hide resolved
app/provider/app.go Outdated Show resolved Hide resolved
@@ -217,17 +219,18 @@ func (k Keeper) SendVSCPacketsToChain(ctx sdk.Context, chainID, channelID string
func (k Keeper) QueueVSCPackets(ctx sdk.Context) {
valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID

// get the bonded validators from the staking module
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docstring is still relevant. Explain better why bondedValidators is needed and why consensusValidators is needed (i.e., for TopN chains).

totalPower := k.stakingKeeper.GetLastTotalPower(ctx)

// Get total total power of the last consensus validator set on the provider
totalPower := k.GetLastTotalProviderConsensusPower(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this changes the behavior. stakingKeeper.GetLastTotalPower gets the total power from the store, while GetLastTotalProviderConsensusPower iterates over 180 keys and recomputes the power every time.

x/ccv/provider/keeper/validator_set_update.go Show resolved Hide resolved

// InitGenesis delegates the InitGenesis call to the underlying x/genutil module,
// however, it returns no validator updates as validator updates will be provided by the provider module.
func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? We don't have a wrapped genutil in the democracy module on the consumer. How is this different?

}
}

k.SetLastProviderConsensusValSet(ctx, reducedValSet)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be exported in ExportGenesis

}
for _, val := range bondedValidators[:maxValidators] {
// create the validator from the staking validator
consAddr, err := val.GetConsAddr()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could potentially reuse CreateConsumerValidator with an empty chainID.

"error", err)
continue
}
nextValidator := types.ConsumerValidator{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General comment but maybe we should not be calling those **Consumer** validators anymore because they're also provider validators.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, agreed. Didn't think of a good, unambigious way for this yet

@@ -262,11 +262,14 @@ func (k Keeper) MakeConsumerGenesis(
// get the bonded validators from the staking module
bondedValidators := k.stakingKeeper.GetLastValidators(ctx)

// get the consensus validators (to compute the minimal power in the top N)
consensusValidators := k.GetLastProviderConsensusValSet(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need both consensus and bonded validators?


reducedValSet := make([]types.ConsumerValidator, len(valSet))
for i, val := range valSet {
consAddr, err := val.GetConsAddr()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment on potentially using CreateConsumerValidator with empty chainID.

@insumity insumity added source: audit To indicate an issue found during an audit. and removed source: audit To indicate an issue found during an audit. labels Jun 4, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 25

Outside diff range and nitpick comments (11)
x/ccv/provider/module_test.go (1)

Line range hint 12-12: Please reorder the imports according to the Golang style guide.

-	"github.com/golang/mock/gomock"
+	"github.com/cosmos/ibc-go/v7/modules/core/24-host"
+	"github.com/golang/mock/gomock"
Tools
golangci-lint

18-18: File is not gci-ed with --skip-generated -s standard -s default -s blank -s dot -s prefix(cosmossdk.io) -s prefix(github.com/cosmos/cosmos-sdk) -s prefix(github.com/cometbft/cometbft) -s prefix(github.com/cosmos/interchain-security) --custom-order (gci)

x/ccv/provider/module.go (1)

Line range hint 30-30: The AppModule struct needs to implement the OnAcknowledgementPacket method to satisfy the IBCModule interface.

+ func (am AppModule) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) (*sdk.Result, error) {
+     // Implementation goes here
+     return nil, nil
+ }
x/ccv/provider/keeper/partial_set_security.go (1)

Line range hint 92-103: Refactor to use more efficient sorting and power summing methods.

The current method for computing the minimum power to opt in is inefficient as it sorts the entire list of validator powers and then computes a running total. Consider using a min-heap for a more efficient approach.

import "container/heap"

// Use a min-heap to keep track of the smallest powers, which allows us to quickly access the top N powers.
type IntHeap []int64

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *IntHeap) Push(x interface{}) {
    *h = append(*h, x.(int64))
}

func (h *IntHeap) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

// In your function:
powersHeap := &IntHeap{}
heap.Init(powersHeap)
for _, val := range bondedValidators {
    heap.Push(powersHeap, val.Power)
    if powersHeap.Len() > int(topN) {
        heap.Pop(powersHeap)
    }
}
minPowerToOptIn := (*powersHeap)[0] // The smallest power in the top N
return minPowerToOptIn, nil
x/ccv/provider/keeper/validator_set_update_test.go (2)

Line range hint 233-233: Consider making "chainID" a constant to avoid repetition.

- chainID := "chainID"
+ const chainID = "chainID"

Line range hint 321-332: Refactor repeated validator creation code into a shared helper function.

- valA := testkeeper.CreateStakingValidator(ctx, mocks, 1, 1)
- valB := testkeeper.CreateStakingValidator(ctx, mocks, 2, 2)
+ valA, valB := createTestValidators(ctx, mocks, []int{1, 2}, []int{1, 2})

// Helper function
func createTestValidators(ctx sdk.Context, mocks MockedKeepers, indices []int, powers []int64) ([]stakingtypes.Validator, []crypto.PublicKey) {
    var validators []stakingtypes.Validator
    var publicKeys []crypto.PublicKey
    for i, index := range indices {
        validator, publicKey := testkeeper.CreateStakingValidator(ctx, mocks, index, powers[i])
        validators = append(validators, validator)
        publicKeys = append(publicKeys, publicKey)
    }
    return validators, publicKeys
}

Also applies to: 363-375, 410-410, 429-429, 444-444

x/ccv/provider/keeper/proposal.go (4)

Line range hint 281-281: Undefined function DiffValidators detected.

Please ensure that the function DiffValidators is defined or imported correctly in this file. If it's defined in another package, you might need to import it.


Line range hint 30-30: Ensure the Keeper type is defined or imported correctly.

It seems there's a recurring issue with the Keeper type being undefined. Please check the import statements and ensure that the Keeper type is correctly defined and accessible in this context.


Line range hint 53-53: Ensure the Keeper type is defined or imported correctly.

This issue with the Keeper type being undefined appears multiple times. It's crucial to resolve this to prevent runtime errors.


Line range hint 129-129: Ensure the Keeper type is defined or imported correctly.

The recurring issue with the Keeper type being undefined needs thorough investigation. Please check all relevant import statements and definitions.

x/ccv/provider/keeper/proposal_test.go (1)

Line range hint 1128-1128: The variable found is reassigned but never used afterwards, which could be an oversight or unnecessary code.

-	found = providerKeeper.PendingConsumerRemovalPropExists(
-		ctx, invalidProp.ChainId, invalidProp.StopTime)
-	require.False(t, found)
+	_ = providerKeeper.PendingConsumerRemovalPropExists(
+		ctx, invalidProp.ChainId, invalidProp.StopTime)
Tools
golangci-lint

10-10: File is not gci-ed with --skip-generated -s standard -s default -s blank -s dot -s prefix(cosmossdk.io) -s prefix(github.com/cosmos/cosmos-sdk) -s prefix(github.com/cometbft/cometbft) -s prefix(github.com/cosmos/interchain-security) --custom-order (gci)

tests/e2e/config.go (1)

90-90: Add documentation for InactiveValsCfg.

It would be beneficial to add a comment explaining the purpose and usage of the InactiveValsCfg configuration type, especially since it relates to the new feature of allowing inactive validators.

mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, true).AnyTimes()

mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB, valC, valD}).AnyTimes()
stakingValA := createStakingValidator(ctx, mocks, 1, 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function createStakingValidator is called multiple times but is not defined in this file or imported. This will cause a compilation error.

To resolve this, ensure that createStakingValidator is either defined in this file or properly imported from another package. If it's intended to be a helper function, you might need to define it like this:

func createStakingValidator(ctx sdk.Context, mocks testkeeper.MockedKeepers, index int, power int64) stakingtypes.Validator {
    // Implementation details here
}

Also applies to: 125-125, 131-131, 137-137, 161-161, 219-219, 222-222, 225-225, 228-228, 309-309, 312-312, 315-315, 318-318, 321-321, 381-381, 391-391

Tools
golangci-lint

119-119: undefined: createStakingValidator (typecheck)

GitHub Check: tests

[failure] 119-119:
undefined: createStakingValidator

Comment on lines +25 to 26
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove duplicate import of "github.com/cosmos/interchain-security/v4/x/ccv/provider/types".

-	"github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
-	providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
+	providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
Tools
golangci-lint

25-25: ST1019: package "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" is being imported more than once (stylecheck)


26-26: ST1019(related information): other import of "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" (stylecheck)

"sort"
"testing"
"time"

stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please organize imports according to the Golang style guide using gci.

-	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

Committable suggestion was skipped due to low confidence.

Tools
golangci-lint

10-10: File is not gci-ed with --skip-generated -s standard -s default -s blank -s dot -s prefix(cosmossdk.io) -s prefix(github.com/cosmos/cosmos-sdk) -s prefix(github.com/cometbft/cometbft) -s prefix(github.com/cosmos/interchain-security) --custom-order (gci)

Comment on lines 1301 to 1308
validators := k.GetLastProviderConsensusValSet(ctx)

// if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch
bondedValidators := k.stakingKeeper.GetLastValidators(ctx)
if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 {
// in a Top-N chain, we automatically opt in all validators that belong to the top N
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN)
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, validators, topN)
if err == nil {
k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower)
k.OptInTopNValidators(ctx, chainID, validators, minPower)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider renaming validators to providerValidators for clarity.

- validators := k.GetLastProviderConsensusValSet(ctx)
+ providerValidators := k.GetLastProviderConsensusValSet(ctx)

This change would make the variable name more descriptive and align with the suggestion from a previous review comment.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
validators := k.GetLastProviderConsensusValSet(ctx)
// if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch
bondedValidators := k.stakingKeeper.GetLastValidators(ctx)
if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 {
// in a Top-N chain, we automatically opt in all validators that belong to the top N
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN)
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, validators, topN)
if err == nil {
k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower)
k.OptInTopNValidators(ctx, chainID, validators, minPower)
validators := k.GetLastProviderConsensusValSet(ctx)
// if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch
if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 {
// in a Top-N chain, we automatically opt in all validators that belong to the top N
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, validators, topN)
if err == nil {
k.OptInTopNValidators(ctx, chainID, validators, minPower)

Comment on lines +111 to +112
wrapped_genutil "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_genutil"
wrapped_staking "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_staking"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider renaming wrapped_genutil and wrapped_staking for clarity.

The current names wrapped_genutil and wrapped_staking are not very descriptive. Consider using names that reflect their functionality or the reason why they are wrapped, which could improve code readability and maintainability.

store := ctx.KVStore(k.storeKey)
bz, err := validator.Marshal()
if err != nil {
panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace panics with proper error handling in the setValidator and getValSet methods to prevent potential chain halts if errors occur. This will enhance the robustness and fault tolerance of the consensus-related methods.

- panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err))
+ return fmt.Errorf("failed to marshal ConsumerValidator: %w", err)
- panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err))
+ return fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)

Also applies to: 89-89

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err))
return fmt.Errorf("failed to marshal ConsumerValidator: %w", err)
Tools
GitHub Check: CodeQL

[warning] 26-26: Panic in BeginBock or EndBlock consensus methods
Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt

Comment on lines +106 to +138
// get the staking validator set
valSet := k.stakingKeeper.GetLastValidators(ctx)

// restrict the set to the first MaxProviderConsensusValidators
maxVals := k.GetParams(ctx).MaxProviderConsensusValidators
if int64(len(valSet)) > maxVals {
valSet = valSet[:maxVals]
}

reducedValSet := make([]types.ConsumerValidator, len(valSet))
for i, val := range valSet {
consAddr, err := val.GetConsAddr()
if err != nil {
k.Logger(ctx).Error("could not create consumer validator",
"validator", val.GetOperator().String(),
"error", err)
continue
}
pubKey, err := val.TmConsPublicKey()
if err != nil {
k.Logger(ctx).Error("could not create consumer validator",
"validator", val.GetOperator().String(),
"error", err)
continue
}
reducedValSet[i] = types.ConsumerValidator{
ProviderConsAddr: consAddr,
Power: k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()),
ConsumerPublicKey: &pubKey,
}
}

k.SetLastProviderConsensusValSet(ctx, reducedValSet)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic to restrict the validator set to the first MaxProviderConsensusValidators is implemented correctly. However, consider adding more detailed logging or error handling around the slicing operation to ensure that any issues with the validator set size are clearly reported and can be debugged easily.

if int64(len(valSet)) > maxVals {
+   k.Logger(ctx).Info("Trimming validator set size", "original_size", len(valSet), "max_size", maxVals)
    valSet = valSet[:maxVals]
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// get the staking validator set
valSet := k.stakingKeeper.GetLastValidators(ctx)
// restrict the set to the first MaxProviderConsensusValidators
maxVals := k.GetParams(ctx).MaxProviderConsensusValidators
if int64(len(valSet)) > maxVals {
valSet = valSet[:maxVals]
}
reducedValSet := make([]types.ConsumerValidator, len(valSet))
for i, val := range valSet {
consAddr, err := val.GetConsAddr()
if err != nil {
k.Logger(ctx).Error("could not create consumer validator",
"validator", val.GetOperator().String(),
"error", err)
continue
}
pubKey, err := val.TmConsPublicKey()
if err != nil {
k.Logger(ctx).Error("could not create consumer validator",
"validator", val.GetOperator().String(),
"error", err)
continue
}
reducedValSet[i] = types.ConsumerValidator{
ProviderConsAddr: consAddr,
Power: k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()),
ConsumerPublicKey: &pubKey,
}
}
k.SetLastProviderConsensusValSet(ctx, reducedValSet)
// get the staking validator set
valSet := k.stakingKeeper.GetLastValidators(ctx)
// restrict the set to the first MaxProviderConsensusValidators
maxVals := k.GetParams(ctx).MaxProviderConsensusValidators
if int64(len(valSet)) > maxVals {
k.Logger(ctx).Info("Trimming validator set size", "original_size", len(valSet), "max_size", maxVals)
valSet = valSet[:maxVals]
}
reducedValSet := make([]types.ConsumerValidator, len(valSet))
for i, val := range valSet {
consAddr, err := val.GetConsAddr()
if err != nil {
k.Logger(ctx).Error("could not create consumer validator",
"validator", val.GetOperator().String(),
"error", err)
continue
}
pubKey, err := val.TmConsPublicKey()
if err != nil {
k.Logger(ctx).Error("could not create consumer validator",
"validator", val.GetOperator().String(),
"error", err)
continue
}
reducedValSet[i] = types.ConsumerValidator{
ProviderConsAddr: consAddr,
Power: k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()),
ConsumerPublicKey: &pubKey,
}
}
k.SetLastProviderConsensusValSet(ctx, reducedValSet)

@@ -685,7 +685,7 @@ func TestValidateGenesisState(t *testing.T) {
nil,
types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0,
time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}),
types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600),
types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600, 180),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the validity of the consumer registration fee parameters.

- sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}
+ sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}

- sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-1000000)}
+ sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000000)}

Ensure that the denomination and amount are correctly set for the consumer registration fee.

Also applies to: 709-709

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600, 180),
types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 180),

Comment on lines +15 to +16
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure the Keeper type is correctly defined and accessible in this file.

It appears that the Keeper type might not be defined or imported correctly, as indicated by the static analysis tool. Please verify the definition and import statements.

@@ -217,17 +219,18 @@
func (k Keeper) QueueVSCPackets(ctx sdk.Context) {
valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID

// get the bonded validators from the staking module
// get the last validator set sent to consensus
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review the usage of undefined functions and ensure they are correctly defined or imported.

The functions DiffValidators and other operations related to validator set management are referenced but not defined in the visible scope. Ensure these functions are properly defined or imported to avoid runtime errors.

Also applies to: 239-239, 303-303

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Outside diff range and nitpick comments (2)
x/ccv/provider/keeper/partial_set_security_test.go (2)

119-142: Check for potential redundant mock expectations

The GetValidatorByConsAddr method of MockStakingKeeper is expected to be called any number of times with specific addresses. This setup could potentially be simplified or explained better to ensure that it's clear why it's needed for each validator and that it does not mask other potential issues in the test logic.


Line range hint 381-391: Optimization suggestion for list manipulations

When manipulating lists such as allowlists and denylists, consider checking if operations can be optimized or if there are redundant operations that could be streamlined. This can improve the performance and readability of the test code.

@@ -1298,13 +1298,14 @@ func (k Keeper) HasToValidate(
return true, nil
}

providerValidators := k.GetLastProviderConsensusValSet(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential data race and inefficiency in validator handling.

The function HasToValidate retrieves the validator set multiple times which can lead to inefficiencies and potential data races in a concurrent environment. Consider retrieving the validator set once at the beginning of the function and reusing it throughout.

+ providerValidators := k.GetLastProviderConsensusValSet(ctx)
  if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 {
      minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, providerValidators, topN)
      if err == nil {
          k.OptInTopNValidators(ctx, chainID, providerValidators, minPower)
      }
  }

Also applies to: 1306-1306

@@ -257,7 +257,7 @@

var minPowerInTopN int64
if found && topN > 0 {
res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx), topN)
res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.GetLastProviderConsensusValSet(ctx), topN)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize error handling in logging.

Currently, the error message in the logging statement does not include the error object, which could provide valuable debugging information. Consider adding the error to the log output to improve error visibility and diagnostics.

- k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err)
+ k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err.Error())
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.GetLastProviderConsensusValSet(ctx), topN)
res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.GetLastProviderConsensusValSet(ctx), topN)
if err != nil {
k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err.Error())
return nil, err
}

Comment on lines +119 to +142
stakingValA := testkeeper.CreateStakingValidator(ctx, mocks, 1, 1)
valA, err := providerKeeper.CreateConsumerValidator(ctx, "chainID", stakingValA) // 10% of the total voting power (can opt out)
require.NoError(t, err)
valAConsAddr := valA.ProviderConsAddr
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(stakingValA, true).AnyTimes()

stakingValB := testkeeper.CreateStakingValidator(ctx, mocks, 2, 2)
valB, err := providerKeeper.CreateConsumerValidator(ctx, "chainID", stakingValB) // 20% of the total voting power (can opt out)
require.NoError(t, err)
valBConsAddr := valB.ProviderConsAddr
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(stakingValB, true).AnyTimes()

stakingValC := testkeeper.CreateStakingValidator(ctx, mocks, 3, 3)
valC, err := providerKeeper.CreateConsumerValidator(ctx, "chainID", stakingValC) // 30% of the total voting power (cannot opt out)
require.NoError(t, err)
valCConsAddr := valC.ProviderConsAddr
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valCConsAddr).Return(stakingValC, true).AnyTimes()

stakingValD := testkeeper.CreateStakingValidator(ctx, mocks, 4, 4)
valD, err := providerKeeper.CreateConsumerValidator(ctx, "chainID", stakingValD) // 40% of the total voting power (cannot opt out)
require.NoError(t, err)
valDConsAddr := valD.ProviderConsAddr
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(stakingValD, true).AnyTimes()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider defining the CreateStakingValidator function or ensuring its import

The function CreateStakingValidator is used multiple times in this test file but is not defined or imported in the visible scope. This could potentially lead to a compilation error unless it is defined elsewhere that's not shown here. Ensure that this function is either defined within this test file or properly imported from the testkeeper package.


return []abci.ValidatorUpdate{}
// EndBlock logic needed for the Validator Set Update sub-protocol
return am.keeper.EndBlockVSU(ctx)

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods Warning

path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Outside diff range and nitpick comments (1)
x/ccv/provider/keeper/relay.go (1)

Line range hint 149-171: Refactor EndBlockVSU for clarity and error handling.

The EndBlockVSU method is complex and handles multiple responsibilities. Consider refactoring it to improve readability and maintainability. Additionally, ensure that all operations have appropriate error handling to prevent potential issues during the block end logic.

func (k Keeper) EndBlockVSU(ctx sdk.Context) []abci.ValidatorUpdate {
+   var err error
    providerUpdates := k.ProviderValidatorUpdates(ctx)
    if providerUpdates == nil {
+       return nil
    }

    k.completeMaturedUnbondingOps(ctx)

    if ctx.BlockHeight()%k.GetBlocksPerEpoch(ctx) == 0 {
        k.QueueVSCPackets(ctx)
-       k.SendVSCPackets(ctx)
+       if err = k.SendVSCPackets(ctx); err != nil {
+           k.Logger(ctx).Error("Failed to send VSC packets", "error", err)
+       }
    }

    return providerUpdates
}

am.keeper.InitGenesis(ctx, &genesisState)

return []abci.ValidatorUpdate{}
return am.keeper.InitGenesis(ctx, &genesisState)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure proper error handling in InitGenesis.

Currently, the InitGenesis method does not handle any errors that might occur during the initialization process. It's important to ensure that any potential issues are properly addressed to prevent runtime errors.

- return am.keeper.InitGenesis(ctx, &genesisState)
+ updates, err := am.keeper.InitGenesis(ctx, &genesisState)
+ if err != nil {
+     return nil, err
+ }
+ return updates, nil
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return am.keeper.InitGenesis(ctx, &genesisState)
return am.keeper.InitGenesis(ctx, &genesisState)
updates, err := am.keeper.InitGenesis(ctx, &genesisState)
if err != nil {
return nil, err
}
return updates, nil

@p-offtermatt
Copy link
Contributor Author

Closing because this has been rebased in PRs targeting the v5 release with Cosmos SDK v50

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C:Testing Assigned automatically by the PR labeler C:x/provider Assigned automatically by the PR labeler C:x/types Assigned automatically by the PR labeler
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants